#pragma once
#include <iostream>

#include <cerrno>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "Log.hpp"
#include "sockaddr.hpp"
#include "Comm.hpp"
#include "nocopy.hpp"

#define DEFAULT_LISTEN_SOCKFD -1
#define DEFAULT_BACKLOG_SIZE 5
#define BUFFERSIZE 1024

class Server : public nocopy //防止拷贝
{
public:
    Server(uint16_t port)
        : _listen_sockfd(DEFAULT_LISTEN_SOCKFD)
        , _sk(port)
    {}
    ~Server()
    {}
    //初始化Server端
    void Init()
    {
        //获取listen套接字，此套接字用于监听client的请求连接
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        //检查返回值
        CheckRet(_listen_sockfd, server_name, func_types[0]);

        //解决server主动关闭，无法重启，bind失败的问题
        int opt = 1;
        setsockopt(_listen_sockfd, 
                   SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt));

        //bind
        int ret_bind = ::bind(_listen_sockfd, (const sockaddr*)(&_sk.addr_in()), _sk.len());
        //检查返回值
        CheckRet(ret_bind, server_name, func_types[1]);

        //这里表示监听client,监听到一个client，将其入队列，再由accept一个一个拿
        int ret_listen = listen(_listen_sockfd, DEFAULT_BACKLOG_SIZE);
        //检查返回值
        CheckRet(ret_listen, server_name, func_types[2]);

    }
    //server开始服务
    void Start()
    {
        //使用循环的原因：server可能会获取多个连接，同时有多个client要通信
        //所以我们就要挨个建立连接（但这里只是单进程，只能获取一个client的连接）
        while(true)
        {
            //输出型参数，获取client的sockaddr配置信息
            sockaddr_in peer; 
            socklen_t len = sizeof(peer);
            //建立连接！
            int sockfd = accept(_listen_sockfd, (sockaddr*)(&peer), &len);
            //这里不需要进行错误排查，没必要，没有收到连接就阻塞
            
            //获取连接成功
            CheckRet(0, server_name, func_types[3]);
            //接收信息并发送回去
            Receive(sockfd);
        }
    }
private:
    int _listen_sockfd;
    Sockaddr _sk;

    void Receive(int sockfd)
    {
        //这里是获取连接成功， 开始接收信息
        char buffer[BUFFERSIZE];
        while(true)
        {
            //接收数据
            ssize_t ret_read = read(sockfd, buffer, sizeof(buffer) - 1);
            //读错误
            if(ret_read < 0)
                CheckRet(ret_read, server_name, func_types[4]);
            //读到了结尾，既client端关闭
            else if(ret_read == 0)
            {
                std::cout << "client exit" << std::endl;
                break;
            }
            //读到数据
            else
            {
                buffer[ret_read] = 0;
                std::cout << "Client# " << buffer << std::endl;
                std::string rebuffer;
                rebuffer += "Server# ";
                rebuffer += buffer;
                ssize_t ret_write = write(sockfd, rebuffer.c_str(), rebuffer.size());
                CheckRet(ret_write, server_name, func_types[5]);
            }
        }
        
        close(sockfd);
    }
};